/*
 * @(#)ResourceTemplateLoader.java $version 2010. 8. 11
 *
 * Copyright 2007-2010 NHN Corp. All rights Reserved. 
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.nhncorp.usf.core.resource;

import java.io.*;
import java.util.Properties;

import javax.servlet.ServletContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;

import com.nhncorp.lucy.common.util.SchemaEntityResolver;
import com.nhncorp.lucy.common.util.XMLDOMUtil;

import freemarker.cache.WebappTemplateLoader;

/**
 * Resource 파일(.res)의 uio content를 Freemarker가 Template으로 생성 하기 위해 사용하는 로더.
 *
 * @author Web Platform Development Team
 */
public class ResourceTemplateLoader extends WebappTemplateLoader {
    static Log log = LogFactory.getLog(ResourceTemplateLoader.class);

    public static final String RESOURCE_EXT = ".res";
    static SchemaEntityResolver entityResolver = new SchemaEntityResolver();

    private boolean reloadable = true;

    static {
        Properties urlMap = new Properties();
        String url = "http://dev.naver.com/xsd/usf/usf-resource.xsd";
        String location = "META-INF/schema/usf-resource.xsd";

        log.debug("Resource Schema URL : " + url);
        urlMap.put(url, location);
        entityResolver.setSchemaProperty(urlMap);
    }

    /**
     * Instantiates a new resource template loader.
     *
     * @param servletContext the servlet context
     * @param path           the path
     */
    public ResourceTemplateLoader(ServletContext servletContext, String path) {
        super(servletContext, path);
    }

    /**
     * Instantiates a new resource template loader.
     *
     * @param serviceContext the service context
     */
    public ResourceTemplateLoader(ServletContext serviceContext) {
        this(serviceContext, "/");
    }

    /**
     * actionId.uioId 의 형태로 입력 받음.
     * 
     * @param name the uio name
     * @return the ResourceFile for uio name
     * @throws IOException the IOException
     */
    @Override
    public Object findTemplateSource(String name) throws IOException {
        if (name == null) {
            throw new IllegalArgumentException("template(use resource) name is null.");
        }

        int uioIdStart = name.indexOf(".[");
        int uioIdEnd = name.indexOf("]", uioIdStart);

        if (uioIdStart < 0) {
            return null;
        }

        if (log.isDebugEnabled()) {
            log.debug("resource file name is [" + name + "]");
        }

        String uioId = name.substring(uioIdStart + 2, uioIdEnd);
        String fileName = name.substring(0, uioIdStart);

        log.debug("uio id is [" + uioId + "]");

        Object resourceFile = super.findTemplateSource(fileName + RESOURCE_EXT);

        if (resourceFile == null) {
            return null;
        }

        return new ResourceFile((File)resourceFile, uioId);
    }

    /**
     * Resource의 lastModified값은 reloadable값에 따라 달라지며, 여기서 리턴되는 값으로 Resource 파일의 Reload여부를 결정.
     * reloadable값이 true일 경우 Resource파일의 lastModified값을 리턴, false일 경우 무조건 0을 리턴.
     * 
     * @param templateSource the templateSource
     * @return the last modified time
     */
    @Override
    public long getLastModified(Object templateSource) {
        return reloadable ? ((File)templateSource).lastModified() : 0;
    }

    /**
     * FreemarkerTemplate를 생성하기 위해 {@link ResourceFile}에 설정된 리소스파일을 파싱하여
     * 해당 Resource(UIO)의 Reader를 리턴.
     * 
     * @param templateSource the templateSource object
     * @param encoding the encoding value
     * @return Reader the Reader object
     * @throws IOException the IOException
     */
    @Override
    public Reader getReader(final Object templateSource, final String encoding) throws IOException {
        log.debug("get reader from resource loader...");

        ResourceFile resourceFile = (ResourceFile)templateSource;
        log.debug("Resource is " + resourceFile.getResourceFile() + "(" + resourceFile.getUioId() + ")");
        String templateBody = getTemplateBody(new FileInputStream(resourceFile.getResourceFile()),
                                              resourceFile.getUioId());

        if (templateBody == null) {
            throw new IOException("Template Content is Null");
        }

        return new StringReader(templateBody);
    }

    /**
     * ResourceFile에서 지정된 UIO영역을 반환.
     *
     * @param inputStream ResourceFile의 InputStream
     * @param uioId       ResourceFile내의 원하는 UIO
     * @return ResourceFile내의 원하는 UIO 문자열
     * @throws IOException the IOException
     */
    @SuppressWarnings("deprecation")
    protected String getTemplateBody(InputStream inputStream, String uioId) throws IOException {
        Document document;
        try {
            document = XMLDOMUtil.loading(inputStream, true, entityResolver);
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        } finally {
            inputStream.close();
        }
        return XMLDOMUtil.getValue(document.getDocumentElement(), " :res[attribute::id=\"" + uioId + "\"]");
    }

    /**
     * ResourceFile class
     */
    private class ResourceFile extends File {
        private static final long serialVersionUID = -2663699914765609574L;
        private File resourceFile;
        private String uioId;

        public ResourceFile(File resourceFile, String uioId) {
            super("");
            this.resourceFile = resourceFile;
            this.uioId = uioId;
        }

        public File getResourceFile() {
            return resourceFile;
        }

        @Override
        public long lastModified() {
            return resourceFile.lastModified();
        }

        public String getUioId() {
            return uioId;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof ResourceFile) {
                ResourceFile resourceFile = (ResourceFile)obj;

                if (this.uioId.equals(resourceFile.uioId) && this.resourceFile.equals(resourceFile.resourceFile)) {
                    return true;
                }

            }

            return false;
        }

        public int hashCode() {
            return super.hashCode();
        }
    }

    /**
     * Checks if is reloadable.
     *
     * @return true, if is reloadable
     */
    public boolean isReloadable() {
        return reloadable;
    }

    /**
     * Sets the reloadable.
     *
     * @param reloadable the new reloadable
     */
    public void setReloadable(boolean reloadable) {
        this.reloadable = reloadable;
    }
}
